#pragma once

#include <atomic>
#include <memory>

#include <boost/mpl/at.hpp>

namespace tb {

namespace safe{

struct AliveTypeTag {};
struct RefCountTypeTag {};

template <typename FunctorType, typename ParamVecType, typename TypeTag, int Args> struct Delegate{};

class AliveOwner {
public:
	typedef std::shared_ptr<std::atomic_bool> alive_value_type;

	typedef std::shared_ptr<std::atomic_int> ref_count_value_type;

	AliveOwner()
		: alive(std::make_shared<alive_value_type::value_type>(true))
		, refCount(std::make_shared<ref_count_value_type::value_type>(true)) {}

	~AliveOwner() {
		alive->store(false);
	}

	void WaitAndDie() {
		alive->store(false);
		int k = 0;
		while (refCount->load(std::memory_order_acquire) > 1) {
			yield(k++);
		}
	}

	bool TryDie() {
		alive->store(false);
		return refCount->load(std::memory_order_acquire);
	}

	void WaitAndForceDie() {
		WaitAndDie();
	}

	bool TryForceDie() {
		return TryDie();
	}

	alive_value_type alive;
	ref_count_value_type refCount;
};

#define SAFE_OBJECT_DELEGATE_IMPL

#define PARAM_COUNT 0
#define TYPE_PARAM_LIST
#define PASS_PARAM_LIST
#include "internal/alive_delegate_impl.inl"
#undef PARAM_COUNT
#undef TYPE_PARAM_LIST
#undef PASS_PARAM_LIST

#define PARAM_COUNT 1
#define TYPE_PARAM_LIST typename boost::mpl::at_c<ParamVecType, 0>::type p1
#define PASS_PARAM_LIST p1
#include "internal/alive_delegate_impl.inl"
#undef PARAM_COUNT
#undef TYPE_PARAM_LIST
#undef PASS_PARAM_LIST

#define PARAM_COUNT 2
#define TYPE_PARAM_LIST typename boost::mpl::at_c<ParamVecType, 0>::type p1, typename boost::mpl::at_c<ParamVecType, 0>::type p2
#define PASS_PARAM_LIST p1, p2
#include "internal/alive_delegate_impl.inl"
#undef PARAM_COUNT
#undef TYPE_PARAM_LIST
#undef PASS_PARAM_LIST

#undef SAFE_OBJECT_DELEGATE_IMPL

class RefCountOwner {
public:
	typedef std::shared_ptr<std::atomic_bool> alive_value_type;

	typedef std::shared_ptr<std::atomic_bool> ref_count_value_type;

	RefCountOwner()
		: alive(std::make_shared<alive_value_type::value_type>(true))
		, refCount(std::make_shared<ref_count_value_type::value_type>(true)) {}

	~RefCountOwner() {
		alive->store(false);
		refCount->store(0, std::memory_order_release);
	}

	void Die() {
		alive->store(false);
		refCount->store(0, std::memory_order_release);
	}

	void WaitAndDie() {
		int k = 0;
		while (refCount->load(std::memory_order_acquire) > 1) {
			yield(k++);
		}
	}

	bool TryDie() {
		if (refCount->load(std::memory_order_acquire) <= 1) {
			Die();
			return true;
		}
		return false;
	}

	void WaitAndForceDie() {
		alive->store(false);
		WaitAndDie();
	}

	void TryForceDie() {
		alive->store(false);
		return TryDie();
	}

	alive_value_type alive;

	ref_count_value_type refCount;
};

#define SAFE_OBJECT_DELEGATE_IMPL

#define PARAM_COUNT 0
#define TYPE_PARAM_LIST
#define PASS_PARAM_LIST
#include "internal/refcount_delegate_impl.inl"
#undef PARAM_COUNT
#undef TYPE_PARAM_LIST
#undef PASS_PARAM_LIST

#define PARAM_COUNT 1
#define TYPE_PARAM_LIST typename boost::mpl::at_c<ParamVecType, 0>::type p1
#define PASS_PARAM_LIST p1
#include "internal/refcount_delegate_impl.inl"
#undef PARAM_COUNT
#undef TYPE_PARAM_LIST
#undef PASS_PARAM_LIST

#define PARAM_COUNT 2
#define TYPE_PARAM_LIST typename boost::mpl::at_c<ParamVecType, 0>::type p1, typename boost::mpl::at_c<ParamVecType, 0>::type p2
#define PASS_PARAM_LIST p1, p2
#include "internal/refcount_delegate_impl.inl"
#undef PARAM_COUNT
#undef TYPE_PARAM_LIST
#undef PASS_PARAM_LIST

#undef SAFE_OBJECT_DELEGATE_IMPL

}

}